JWT (JSON Web Token) হল একটি স্ট্যান্ডার্ড টোকেন-ভিত্তিক প্রমাণীকরণ এবং অনুমোদনের পদ্ধতি, যেখানে একটি কমপ্যাক্ট এবং সেলফ-কনটেইনড টোকেন ব্যবহার করা হয়। Spring Security-তে JWT Authentication এবং Authorization কনফিগার করার জন্য নিচে একটি স্টেপ-বাই-স্টেপ উদাহরণ দেওয়া হলো।
1. JWT-এর মূল বৈশিষ্ট্য
- Stateless Authentication: সার্ভার সেশন মেইনটেইন করে না, বরং ক্লায়েন্ট-সাইডে টোকেন সংরক্ষণ করে।
- Self-contained Token: টোকেনে সমস্ত তথ্য এনকোড করা থাকে (যেমন ইউজার আইডি, রোল)।
- Security: টোকেনটি সাইন করা থাকে যাতে এটি পরিবর্তন করা না যায়।
2. প্রজেক্ট ডিপেন্ডেন্সি
Spring Boot এবং JWT লাইব্রেরি যুক্ত করতে pom.xml বা build.gradle-এ ডিপেন্ডেন্সি যোগ করুন।
Maven Dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Gradle Dependency
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'io.jsonwebtoken:jjwt:0.9.1'
3. JWT Token তৈরি এবং যাচাই করার ক্লাস
i. JWT Utility ক্লাস
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtil {
private String secretKey = "mySecretKey";
private long expirationTime = 1000 * 60 * 60; // 1 hour
// টোকেন তৈরি
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + expirationTime))
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
// টোকেন থেকে ইউজারনেম বের করা
public String extractUsername(String token) {
return getClaims(token).getSubject();
}
// টোকেন এক্সপায়ার্ড কিনা যাচাই
public boolean isTokenExpired(String token) {
return getClaims(token).getExpiration().before(new Date());
}
// টোকেন যাচাই
public boolean validateToken(String token, String username) {
return (username.equals(extractUsername(token)) && !isTokenExpired(token));
}
// প্রাইভেট মেথড: Claims বের করা
private Claims getClaims(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody();
}
}
4. Spring Security Configuration
i. Security Config ক্লাস
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate", "/register").permitAll() // Public endpoints
.anyRequest().authenticated(); // Others require authentication
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("password")).roles("USER")
.and()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN");
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
5. Authentication Controller
i. Token প্রদান করার জন্য Controller
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/authenticate")
public class AuthController {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private JwtUtil jwtUtil;
@PostMapping
public String generateToken(@RequestBody AuthRequest authRequest) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
return jwtUtil.generateToken(authRequest.getUsername());
} catch (Exception e) {
throw new RuntimeException("Invalid username/password");
}
}
}
// DTO for authentication request
class AuthRequest {
private String username;
private String password;
// Getters and Setters
}
6. JWT Filter
i. JWT Token যাচাই করার জন্য Filter
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) {
String authorizationHeader = request.getHeader("Authorization");
String token = null;
String username = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
token = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(token);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (jwtUtil.validateToken(token, username)) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
username, null, new ArrayList<>());
authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authToken);
}
}
chain.doFilter(request, response);
}
}
7. Filter Configuration
i. Filter Spring Security Config-এ যুক্ত করা
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtFilter jwtFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
}
8. Test Endpoints
Public Endpoint:
@GetMapping("/public")
public String publicEndpoint() {
return "This is a public endpoint";
}
Secured Endpoint:
@GetMapping("/secured")
public String securedEndpoint() {
return "This is a secured endpoint";
}
JWT ব্যবহার করার প্রক্রিয়া
/authenticateএPOSTরিকোয়েস্ট পাঠিয়ে টোকেন জেনারেট করুন।- টোকেনটি প্রতিটি সিকিউরড রিকোয়েস্টে Authorization Header এ
Bearer <token>আকারে পাঠান। - সিকিউরড এন্ডপয়েন্টে এক্সেস পান।
উপসংহার
Spring Security-তে JWT Authentication এবং Authorization ইমপ্লিমেন্ট করলে আপনার অ্যাপ্লিকেশন stateless এবং নিরাপদ হয়। উপরোক্ত উদাহরণটি ব্যবহার করে আপনি সহজেই একটি JWT-ভিত্তিক Authentication সিস্টেম তৈরি করতে পারবেন।
Read more